package com.marinesky.mstools.file;

/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
             .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         佛祖保佑       永无BUG
*/

import com.marinesky.mstools.codec.MD5Util;
import com.marinesky.mstools.common.Constant;
import org.apache.commons.lang.StringUtils;

import java.io.*;
import java.net.URL;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 *
 */
public class FileUtil {


    /**
     * 获取文件
     * 可以classpath:项目中文件
     * 也可以是直接路径
     * @param filePath
     * @return
     */
    public static File getFile(String filePath){
        File file=null;
        try{
            if(StringUtils.isBlank(filePath)){
                return  file;
            }
            if(filePath.toLowerCase().startsWith(Constant.File.CLASSPATH_PREFIX)){
                String relativePath=filePath.replaceFirst(Constant.File.CLASSPATH_PREFIX, "");
                filePath=FileUtil.class.getResource("/").getPath()+relativePath;
                file=new File(filePath);
            }
            file=new File(filePath);
        }catch (Exception e){
            e.printStackTrace();
        }

        return file;
    }


    /**
     * 获取文件的名字(不包括后缀)
     * @param file
     * @return
     */
    public static String getFileName(File file) {
        String fileName="";
        String name=file.getName(); //
        if(StringUtils.isNotBlank(name))
        {
            fileName=name;
            int i=name.lastIndexOf(Constant.File.FILE_DOT);
            if(i>-1 && i<name.length())
                fileName=name.substring(0,i);
        }
        return fileName;
    }

    /**
     * 获取文件后续(不包括点)
     * @param file
     * @return
     */
    public static String getExtension(File file) {
        String extension="";
        String name=file.getName();
        if(StringUtils.isNotBlank(name))
        {
            int i=name.lastIndexOf(Constant.File.FILE_DOT);
            if(i>-1 && i<name.length()-1)
                extension=name.substring(i+1).toLowerCase();
        }
        return extension;
    }


    /**
     * 获取文件md5值
     * @param file
     * @return
     */
    public static String getMD5ByFile(File file){
        String value=null;
        try{

            if(file==null || !file.exists() || file.isDirectory()){
                throw new FileNotFoundException("the file is null or not exists or is directory!");
            }
            FileInputStream fileInputStream=new FileInputStream(file);
            MappedByteBuffer byteBuffer=fileInputStream.getChannel().map(FileChannel.MapMode.READ_ONLY,0,file.length());
            value= MD5Util.encodeMD5(byteBuffer);
        }catch (Exception e)
        {
            e.printStackTrace();
        }
        return value;
    }


    /**
     * 获取文件md5值
     * @param filePath
     * @return
     */
    public static String getMD5ByFile(String filePath){
        File file=getFile(filePath);
        return getMD5ByFile(file);
    }

    /**
     *
     * @param file
     * @return
     */
    private static long getFileLength(File file){
        long result=0L;
        try {
            if (file == null || !file.exists() || file.isDirectory()) {
                throw new FileNotFoundException("the file is null or not exists or is directory!");
            }
            result=file.length();
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return  result;
    }

    /**
     * 获取文件或文件夹字节数(Byte)
     * @param files
     * @return
     */
    public static long getFilesLength(File files){
        long result=0L;
        try {
            if (files == null || !files.exists()) {
                throw new FileNotFoundException("the file is null or not exists!");
            }
            if(files.isDirectory()){
                File[] listFiles=files.listFiles();
                for(File file:listFiles){
                    result=result+getFilesLength(file);
                }
            }
            else
            {
                result=getFileLength(files);
            }

        }
        catch (Exception e){
            e.printStackTrace();
        }
        return  result;
    }


    /**
     * 获取文件或文件夹大小(Byte,KB,MB,GB)
     * 如30B,2KB,1MB,2GB,1.22TB
     * @param files
     * @return
     */
    public static String getFileSize(File files){
        long size=getFilesLength(files);
        DecimalFormat df = new DecimalFormat("####.##");
        float f;
        String formatStr=null;
        //使用位运算更快
        if(size>=0 && size<1<<10){
            //0-1024(包括0,不包括1024)范围内用BIT单位
            formatStr=df.format(size)+Constant.File.UNIT_BYTE;
        }
        else if(size>=1<<10 && size<1<<20){
            //1024-1024^2(包括1024,不包括1024*1024)范围用KB单位
            f=(float)((float)size/((float)(1<<10)));
            formatStr=df.format(f)+Constant.File.UNIT_KB;
        }
        else if(size>=1<<20 && size<1<<30){
            //1024^2-1024^3(同上)范围用MB单位
            f=(float)((float)size/((float)(1<<20)));
            formatStr=df.format(f)+Constant.File.UNIT_MB;
        }
        else if(size>=1<<30 && size<1L<<40){
            //1024^2-1024^3(同上)范围用GB单位
            f=(float)((float)size/((float)(1<<30)));
            formatStr=df.format(f)+Constant.File.UNIT_GB;
        }
        else{
            //超过上述范围统一用TB表示(1L是因为int占用32位,L表示Long占64位)
            f=(float)((float)size/((float)(1L<<40)));
            formatStr=df.format(f)+Constant.File.UNIT_TB;
        }

       return  formatStr;
    }

    /**
     * 把文件读byte数组
     * @param file
     * @return
     */
    public static byte[] getBytesFromFile(File file){
        try {
            if (file == null || !file.exists() || file.isDirectory()) {
                throw new FileNotFoundException("the file is null or not exists or is directory!");
            }
            FileInputStream stream = new FileInputStream(file);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] b = new byte[1<<10];
            int n;
            while ((n = stream.read(b)) != -1){
                out.write(b, 0, n);
            }
            stream.close();
            out.close();
            return out.toByteArray();
        } catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * byte数组转文件
     * @param b
     * @param outputFile
     * @return
     */
    public static File getFileFromBytes(byte[] b, String outputFile) {
        BufferedOutputStream stream = null;
        File file = null;
        try {
            file = new File(outputFile);
            FileOutputStream fstream = new FileOutputStream(file);
            stream = new BufferedOutputStream(fstream);
            stream.write(b);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
        return file;
    }


    /**
     * 创建文件
     * @param file
     */
    public static void createFile(File file){

        try {
            if(!file.exists()){
                File parent=file.getParentFile();
                if(parent!=null && !parent.exists()){
                    parent.mkdirs();
                }
                file.createNewFile();
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }


    /**
     * 创建文件
     * 可以classpath:项目中文件
     * 也可以是直接路径
     * @param filePath
     */
    public static void createFile(String filePath){
        File file=getFile(filePath);
        createFile(file);
    }

    /**
     * 删除文件或文件夹(包括子文件及其目录)
     * @param file -- 文件或文件夹
     */
    public static void deleteFile(File file){
        if(file!=null && file.exists()){
            if(file.isDirectory()){
                File childrenFiles[]=file.listFiles();
                for (File childrenFile:childrenFiles){
                    deleteFile(childrenFile);
                }
            }
            file.delete();
        }

    }

    /**
     * 删除文件或文件夹(包括子文件及其目录)
     * @param filePath -- 文件或文件夹
     */
    public static void deleteFile(String filePath){
        File file=getFile(filePath);
        if(file!=null && file.exists()){
            if(file.isDirectory()){
                File childrenFiles[]=file.listFiles();
                for (File childrenFile:childrenFiles){
                    deleteFile(childrenFile);
                }
            }
            file.delete();
        }

    }


    /**
     * 指定编码读取文件内容到字符串
     * @param file --文件
     * @param charset --编码
     * @return String --文件的内容
     */
    public static String readFileToString(File file,String charset){
        try {
            if (file == null || !file.exists() || file.isDirectory()) {
                throw new FileNotFoundException("the src is null or not exists or is directory!");
            }
            if(StringUtils.isBlank(charset) || !Charset.isSupported(charset)){
                throw new IllegalArgumentException(" the charset is blank or not support! ");
            }
            InputStreamReader inputStreamReader=new InputStreamReader(new FileInputStream(file),charset);
            BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
            String str="";
            StringBuffer stringBuffer=new StringBuffer();
            //按行读取文件内容
            while ((str=bufferedReader.readLine())!=null){
                stringBuffer.append(str);
                stringBuffer.append(Constant.SystemsPlatform.LINEBREAK);
            }
            return stringBuffer.toString();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 默认以utf-8编码读取文件到字符串
     * @param file --文件
     * @return String --文件的内容
     */
    public static String readFileToString(File file){
        return readFileToString(file,Constant.Charset.UTF8);
    }


    /**
     * 指定编码读取文件内容到字符串
     * @param filePath --文件路径
     * @param charset --编码
     * @return String --文件的内容
     */
    public static String readFileToString(String filePath,String charset){
        File file=getFile(filePath);
        return  readFileToString(file,charset);
    }


    /**
     * 默认以utf-8编码读取文件到字符串
     * @param filePath --文件路径
     * @return String --文件的内容
     */
    public static String readFileToString(String filePath){
        return readFileToString(filePath,Constant.Charset.UTF8);
    }


    /**
     * 文件单个复制
     * @param srcFile  --源文件
     * @param destFile --目标文件
     */
    public static void copySingleFile(File srcFile,File destFile){

        FileInputStream srcInputStream=null;
        FileChannel srcChannel=null;

        FileOutputStream destOutputStream=null;
        FileChannel destChannel=null;
        try {
            if (srcFile == null || !srcFile.exists() || srcFile.isDirectory()) {
                throw new FileNotFoundException("the srcFile is null or not exists or is directory!");
            }
            if (destFile == null || destFile.isDirectory()) {
                throw new FileNotFoundException("the destFile is null  or is directory!");
            }
            createFile(destFile);
            srcInputStream=new FileInputStream(srcFile);
            //获取源文件的通道
            srcChannel=srcInputStream.getChannel();

            destOutputStream=new FileOutputStream(destFile);
            //获取目标文件通道
            destChannel=destOutputStream.getChannel();

            //将源通道与目标连接,并把源通道数据写到目标通道
            srcChannel.transferTo(0,srcChannel.size(),destChannel);

        } catch (Exception e){
            e.printStackTrace();
        }finally {
            try{
                srcInputStream.close();
                srcChannel.close();
                destOutputStream.close();
                destChannel.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }

    }



    /**
     * 文件夹复制(包括了子目录和文件夹)
     * @param srcFolder --源文件夹
     * @param destFolder  --目标文件夹
     */
    public static void copyFolder(File srcFolder,File destFolder){
        try{
            if(srcFolder==null || !srcFolder.exists() || srcFolder.isFile()){
                throw new FileNotFoundException("the srcFolder is null or not exists or is file!");
            }
            if(destFolder==null || destFolder.isFile()){
                throw new FileNotFoundException("the destFolder is null or is file!");
            }
            if(!destFolder.exists()){
                destFolder.mkdirs();
            }
            File[] files=srcFolder.listFiles();
            String srcPath=srcFolder.getAbsolutePath();
            String destPath=destFolder.getAbsolutePath();
            for(File file:files){
                if(file.isFile()){
                    //保持路径与源文件目录一致
                    String filePath=file.getAbsolutePath();
                    String destFilePath=destPath+filePath.substring(srcPath.length(),filePath.length());
                    copySingleFile(file, new File(destFilePath));
                }
                else{
                    //保持复制前后目录一致
                    String folderPath=file.getAbsolutePath();
                    String destFolderPath=destPath+folderPath.substring(srcPath.length(), folderPath.length());
                    copyFolder(file,new File(destFolderPath));
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    /**
     * 复制文件或文件夹
     * @param srcFile  --源文件或文件夹
     * @param destFile --目标文件或文件夹
     */
    public static void copyFile(File srcFile,File destFile){
       try{
           if(srcFile==null || !srcFile.exists()){
               throw new FileNotFoundException("the srcFile is null or not exists !");
           }
           if(srcFile.isDirectory()){
                copyFolder(srcFile,destFile);
           }
           else{
               copySingleFile(srcFile,destFile);
           }

       }catch (Exception e){
           e.printStackTrace();
       }
    }


    /**
     * 复制文件或文件夹
     * @param srcFilePath  --源文件或文件夹路径
     * @param destFilePath  --目标文件或文件夹路径
     */
    public static void copyFile(String srcFilePath,String destFilePath){
        File srcFile=getFile(srcFilePath);
        File destFile=getFile(destFilePath);
        copyFile(srcFile,destFile);
    }

    /**
     * 复制文件或文件夹
     * @param srcFile  --源文件或文件夹
     * @param destFilePath  --目标文件或文件夹路径
     */
    public static void copyFile(File srcFile,String destFilePath){
        File destFile=getFile(destFilePath);
        copyFile(srcFile,destFile);
    }

    /**
     * 复制文件或文件夹
     * @param srcFilePath  --源文件或文件夹路径
     * @param destFile      --目标文件或文件夹路径
     */
    public static void copyFile(String srcFilePath,File destFile){
        File srcFile=getFile(srcFilePath);
        copyFile(srcFile,destFile);
    }



    /**
     * 移动文件或文件夹
     * @param srcFile  --源文件或文件夹
     * @param destFile --目标文件或文件夹
     */
    public static void moveFile(File srcFile,File destFile){
        copyFile(srcFile,destFile);
        deleteFile(srcFile);
    }

    /**
     * 移动文件或文件夹
     * @param srcFile  --源文件或文件夹
     * @param destFilePath --目标文件或文件夹路径
     */
    public static void moveFile(File srcFile,String destFilePath){
        copyFile(srcFile,destFilePath);
        deleteFile(srcFile);
    }


    /**
     * 移动文件或文件夹
     * @param srcFilePath  --源文件或文件夹路径
     * @param destFile --目标文件或文件夹
     */
    public static void moveFile(String srcFilePath,File destFile){
        copyFile(srcFilePath,destFile);
        deleteFile(srcFilePath);
    }


    /**
     * 移动文件或文件夹
     * @param srcFilePath  --源文件或文件夹路径
     * @param destFilePath --目标文件或文件夹路径
     */
    public static void moveFile(String srcFilePath,String destFilePath){
        copyFile(srcFilePath,destFilePath);
        deleteFile(srcFilePath);
    }


    /**
     * 获取文件最后一次修改时间
     * @param filePath  --文件或文件夹路径
     * @return
     */
    public static Date getLastModifyTime(String filePath){
        File file=getFile(filePath);
        return getLastModifyTime(file);
    }

    /**
     * 获取文件最后一次修改时间
     * @param file --文件或文件夹
     * @return
     */
    public static Date getLastModifyTime(File file){
        Date result=null;
        try{
            if(file==null || !file.exists()){
                throw new FileNotFoundException("the file is null or not exists !");
            }
            long time=file.lastModified();
            result =new Date(time);
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 把字符串写入文件
     * @param content  --写入内容
     * @param file     --文件
     * @param charset   --编码
     */
    public static void writeStringToFile(String content,File file,String charset){
        try{
            Writer ow = new OutputStreamWriter(new FileOutputStream(file), charset);
            ow.write(content.toString());
            ow.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     *
     * @param srFilePath
     */
    public static void convertGBKToUTF8(String srFilePath){
        try{
            File srcFile=FileUtil.getFile(srFilePath);
            String content=readFileToString(srcFile,"GBK");
            writeStringToFile(content,srcFile,"UTF-8");
        }catch (Exception e){
            e.printStackTrace();
        }
    }


}

